#ifndef ICCINFO_H
#define ICCINFO_H

#include <QObject>
#include <QDateTime>

/*!
 \brief
 DF01卡片发行基本数据文件，结构定义参考：
 - 《收费公路联网收费技术要求》（未核对）
 - 《山东高速信联卡平台系统-信联卡卡片结构设计》
*/
#pragma pack(push, 1)
struct RawIccFile0015
{
    quint8 cardIssuer[8];                   /*!  8 发卡方标识，前4字节是省份汉字 */
    quint8 cardType;                        /*!  9 卡片类型，22-储值卡；23-记账卡 */
    quint8 cardVersion;                     /*! 10 卡片版本号 */
    quint8 cardNetwork[2];                  /*! 12 卡片网络编号，BCD编码，山东省厅发行的是3701，信联卡是3702 */
    quint8 cardNo[8];                       /*! 20 CPU卡片内部编号 */
    quint8 startTime[4];                    /*! 24 启用时间 */
    quint8 expireTime[4];                   /*! 28 到期时间 */
    quint8 vehicleLicencePlateNumber[12];   /*! 40 车牌号码 */
    quint8 userType;                        /*! 41 用户类型 */
    quint8 vehicleLicencePlateColor;        /*! 42 车牌颜色 - 国标为2字节整型，山东分出1个字节存储车型 */
    quint8 vehicleType;                     /*! 43 车型/鲁通C卡免费车种，低4位存车型，高4位存鲁通C卡免费车种 - 限山东 */
};
#pragma pack(pop)

/*!
 \brief
 DF02信联卡基本信息文件，结构定义参考：
 - 《山东高速信联卡平台系统-信联卡卡片结构设计》
*/
#pragma pack(push, 1)
struct RawIccXinlian0015
{
    quint8 cardIssuer[2];                   /*!  2 发卡方代码，BCD编码，填写运营商标识 */
    quint8 region[2];                       /*!  4 地域代码（城市代码），BCD编码，填写各地市区域代码，例如：山东3700 */
    quint8 industry[2];                     /*!  6 行业代码，BCD编码，预留 */
    quint8 rfu[2];                          /*!  8 RFU，预留 */
    quint8 appType;                         /*!  9 应用类型标识，1表示启用，默认填写0x01 */
    quint8 appVersion;                      /*! 10 应用版本，记录发行版本号，0x10代表支持复合消费，其它保留 */
    quint8 mutualFlag[2];                   /*! 12 互通标识（参与互通城市的标识），默认填写地域代码 */
    quint8 cardNo[8];                       /*! 20 CPU卡片内部编号（应用序列号），卡片应用ID，BCD编码 */
    quint8 startTime[4];                    /*! 24 启用时间 */
    quint8 expireTime[4];                   /*! 28 到期时间 */
    quint8 cardType;                        /*! 29 卡片主类型，普通储值卡-00；贷记卡（超级Ａ卡）-10 */
    quint8 subType;                         /*! 30 卡片子类型，默认填写 00 */
    quint8 scope[2];                        /*! 32 卡片应用范围代码，BCD，信联卡自己分配。分别对应某个或某一批商户。 */
    quint8 limitType;                       /*! 33 限制种类标志，0-无限制，1-包含在“使用限制信息文件”中的商户，才允许使用该卡。*/
    quint32 balanceLimit;                   /*! 37 电子钱包限额，单位为分 */
    quint8 reserved[13];                    /*! 50 保留 */
};
#pragma pack(pop)

/*!
 \brief
 DF02信联卡复合消费文件，结构定义参考：
 - 《山东高速信联卡平台系统-信联卡卡片结构设计》
*/
#pragma pack(push, 1)
struct RawIccXinlian0017
{
    quint8 flag;                            /*!  1 复合消费标志，09：全国通用消费 */
    quint8 dataLen;                         /*!  2 复合消费数据长度 */
    quint8 locked;                          /*!  3 复合消费锁定标志 */
    quint8 version;                         /*!  4 复合消费记录版本号(0x01) */
    quint8 transFlag;                       /*!  5 交易标识（0x00标识已完成） */
    quint8 termNo[6];                       /*! 11 终端机编号	*/
    quint32 moneyToDeductBigEndian;         /*! 15 金额（用于补扣） */
    quint8 deductTime[7];                   /*! 22 交易日期时间，BCD，YYYYMMDDHHMMSS */
    quint8 mac1[4];                         /*! 26 通用复合消费记录MAC */
    quint8 reserved[22];                    /*! 48 自定义数据	*/
};
#pragma pack(pop)

#pragma pack(push, 1)
struct RawIccInitForPurchaseRlt
{
    quint32 balanceBeforeInBigEndian;       /*!  4 扣款前余额 */
    quint16 transNoInBigEndian;             /*!  6 电子钱包脱机交易序号 */
    quint8 overdraftLimit[3];               /*!  9 透支限额 */
    quint8 keyVersion;                      /*! 10 密钥版本号 */
    quint8 algorithmId;                     /*! 11 算法标识 */
    quint8 random[4];                       /*! 15 随机数 */
};
#pragma pack(pop)


#pragma pack(push, 1)
struct RawIccMac1Rlt
{
    quint32 termTransNoInBigEndian;         /*!  4 终端脱机交易序号 */
    quint8 mac1[4];                         /*!  8 Mac1 */
};
#pragma pack(pop)


#pragma pack(push, 1)
struct RawIccTacRlt
{
    quint8 tac[4];                          /*!  4 TAC */
    quint8 mac2[4];                         /*!  8 Mac2 */
};
#pragma pack(pop)

class IccInfo : public QObject
{
public:
    explicit IccInfo(QObject *parent = 0);
    IccInfo(const IccInfo &iccInfo) : QObject(iccInfo.parent())
    {
        *this = iccInfo;
    }

    void operator =(const IccInfo &iccInfo)
    {
        m_rawIccFile0015=iccInfo.m_rawIccFile0015;
        m_rawIccXinlian0015=iccInfo.m_rawIccXinlian0015;
        m_rawIccInitForPurchaseRlt=iccInfo.m_rawIccInitForPurchaseRlt;
        m_rawIccMac1Rlt=iccInfo.m_rawIccMac1Rlt;
        m_rawIccTacRlt=iccInfo.m_rawIccTacRlt;
        memcpy(&m_xinlianTermNo, &iccInfo.m_xinlianTermNo, sizeof(m_xinlianTermNo));
        memcpy(&m_xinlianPsamId, &iccInfo.m_xinlianPsamId, sizeof(m_xinlianPsamId));
        m_nGbBalance=iccInfo.m_nGbBalance;
        m_nXinlianBalance=iccInfo.m_nXinlianBalance;
        m_startTime=iccInfo.m_startTime;
        m_expireTime=iccInfo.m_expireTime;
        m_nMoneyToDeduct=iccInfo.m_nMoneyToDeduct;
        m_deductTime=iccInfo.m_deductTime;
        m_isXinlianCard=iccInfo.m_isXinlianCard;
    }

    QString toString() const;

public:
    // 国标0015文件长度
    const static quint8 DF01_0015_SIZE=43;
    // 国标余额长度
    const static quint8 DF01_BALANCE_SIZE=4;

    // 信联0015文件长度
    const static quint8 DF02_0015_SIZE=50;

public:
    /*!
     \brief
     设置DF01卡片发行基本数据文件，更新此文件时将清空信联卡相关文件。
     \param icc0015
     DF01卡片发行基本数据文件。
     \param sizeOfIcc0015
     DF01卡片发行基本数据文件长度。
     \return
     执行结果，true表示成功。
    */
    bool setIcc0015(const quint8 *icc0015, quint8 sizeOfIcc0015);
    /*!
     * \brief setIcc0015
     * \param icc0015
     * \return 执行结果，true表示成功。
     */
    bool setIcc0015(const QString &icc0015);

    bool setGbBalance(const quint8 *rawBalance, quint8 sizeOfBalance);

    bool setXinlianBaseInfo(const quint8 *xinlian0015, quint8 sizeOfXinlian0015);
    bool setXinlianBaseInfo(const QString &xinlian0015);


    /*!
     * \brief 判断信联卡是否在有效期内。
     * \return true表示有效。
     */
    bool isDateValid();

    /*!
     * \brief 获取启用时间。
     */
    inline QDate getStartTime()
    {
        return this->m_startTime;
    }

    /*!
     * \brief 获取到期时间。
     */
    QDate getExpireTime()
    {
        return this->m_expireTime;
    }

    /*!
     * \brief 获取是否为山东发行的卡片
     * \return true表示是。
     */
    inline bool isFromShandong()
    {
        // "\xc9\xbd\xb6\xab": "山东"的GB2312编码
        return 0==memcmp("\xc9\xbd\xb6\xab", this->m_rawIccFile0015.cardIssuer, 4);
    }

    /*!
     * \brief 根据icc0015内的发卡方标识和卡片网络编号判断卡片是否为信联卡。
     * \return true表示是。
     */
    inline bool isXinlianCardBasedOnGB()
    {
        return (0x37==this->m_rawIccFile0015.cardNetwork[0]) &&
               (0x02==this->m_rawIccFile0015.cardNetwork[1]) &&
               this->isFromShandong();
    }

    inline void setIsXinlianCard(bool isXinlianCard)
    {
        this->m_isXinlianCard = isXinlianCard;
    }

    inline bool getIsXinlianCard() const
    {
        return this->m_isXinlianCard;
    }

    /*!
     * \brief 获取网络编号（4位）
     */
    QString getGBNetworkNo() const;

    /*!
     * \brief 获取国标卡类型
     */
    int getGBCardType() const
    {
        return this->m_rawIccFile0015.cardType;
    }

    /*!
     * \brief 获取国标卡号（16位）
     */
    QString getGBCardNo() const;

    /*!
     * \brief 获取国标卡号（20位，含网络编号）
     */
    QString getGBCardNoWithNetworkNo() const;

    /*!
     * \brief 获取国标15文件中的车牌号
     */
    QString getVehicleLicencePlateNumber() const;

    /*!
     * \brief 获取国标15文件中的车牌颜色
     */
    int getVehicleLicencePlateColor() const;

    /*!
     * \brief 获取信联卡卡号（16位）
     */
    QString getXinlianCardNo() const;

protected:
    /*!
     * \brief DF01卡片发行基本数据文件，用于判断是否为信联卡、辅助判别车型（车牌与OBU内一致时可以此车型从卡内扣款）。
     */
    RawIccFile0015 m_rawIccFile0015;

    quint32 m_nGbBalance;

    /*!
     \brief
     DF02信联卡基本信息文件。
    */
    RawIccXinlian0015 m_rawIccXinlian0015;

    RawIccInitForPurchaseRlt m_rawIccInitForPurchaseRlt;

    RawIccMac1Rlt m_rawIccMac1Rlt;

    RawIccTacRlt m_rawIccTacRlt;

    /*!
     * \brief 启用时间，以信联卡基本信息文件为准。
     */
    QDate m_startTime;

    /*!
     * \brief 到期时间，以信联卡基本信息文件为准。
     */
    QDate m_expireTime;

    /*!
     * \brief 是否为信联卡，执行消费初始化后有效。
     */
    bool m_isXinlianCard;
};

Q_DECLARE_METATYPE(IccInfo)

#endif // ICCINFO_H
